package app

import (
	"gitee.com/zhucheer/orange/internal"
	"net/http"
	"strings"
	"sync"
)

const (
	skind kind = iota // static
	pkind             // param
	akind             // any
)

type (
	RouterGroup struct {
		prefix      string
		middlewares []MiddleWare
	}
	Router struct {
		tree          *node
		maxParam      *int
		middlewareMap map[string][]MiddleWare
		lock          sync.RWMutex
	}
	node struct {
		kind          kind
		label         byte
		prefix        string
		parent        *node
		children      children
		ppath         string
		pnames        []string
		methodHandler *methodHandler
	}
	kind     uint8
	children []*node

	methodHandler struct {
		connect HandlerFunc
		delete  HandlerFunc
		get     HandlerFunc
		head    HandlerFunc
		options HandlerFunc
		patch   HandlerFunc
		post    HandlerFunc
		put     HandlerFunc
		trace   HandlerFunc
		any     HandlerFunc
	}
	customHttpHandler struct {
		patten  string
		handler http.Handler
	}
)

var routers = &Router{
	tree: &node{
		methodHandler: new(methodHandler),
	},
	middlewareMap: make(map[string][]MiddleWare),
	maxParam:      new(int),
}

// 自定义httpHandler
var httpHandlerList []customHttpHandler

// 静态资源目录
var assetsHandlerList []customHttpHandler

func NewRouter(prefix string, middlewares ...MiddleWare) *RouterGroup {
	middlewares = reverseMiddleWare(middlewares)
	return &RouterGroup{
		prefix:      prefix,
		middlewares: middlewares,
	}
}

func NewHttpHandler(patten string, handler http.Handler) {
	httpHandlerList = append(httpHandlerList, customHttpHandler{patten, handler})
}

func NewAssetsHandler(patten string, filePath string, autoindex FileServerType) {
	patten = "/" + strings.Trim(patten, "/") + "/"

	if autoindex == AutoIndex {
		assetsHandlerList = append(assetsHandlerList, customHttpHandler{
			patten,
			http.StripPrefix(patten, http.FileServer(http.Dir(filePath))),
		})
	} else {
		assetsHandler := noDirListingHandler(http.StripPrefix(patten, http.FileServer(http.Dir(filePath))), patten, filePath)
		assetsHandlerList = append(assetsHandlerList, customHttpHandler{patten, assetsHandler})
	}
}

// GroupRouter 子群组路由
func (r *RouterGroup) GroupRouter(prefix string, middlewares ...MiddleWare) *RouterGroup {
	prefix = r.prefix + prefix

	middlewaresNew := append(middlewares, r.middlewares...)
	return &RouterGroup{
		prefix:      prefix,
		middlewares: middlewaresNew,
	}
}

// Assets 注册静态资源目录
func (r *RouterGroup) ASSETS(patten string, fielPath string, autoindex FileServerType) {
	patten = strings.TrimRight(r.prefix+patten, "/")
	NewAssetsHandler(patten, fielPath, autoindex)
}

// GET 注册 get 请求
func (r *RouterGroup) GET(patten string, handler func(ctx *Context) error) {
	r.appRoute("GET", patten, handler)
}

// POST 注册 post 请求
func (r *RouterGroup) POST(patten string, handler func(ctx *Context) error) {
	r.appRoute("POST", patten, handler)
}

// PUT 注册 put 请求
func (r *RouterGroup) PUT(patten string, handler func(ctx *Context) error) {
	r.appRoute("PUT", patten, handler)
}

// PATCH 注册 patch 请求
func (r *RouterGroup) PATCH(patten string, handler func(ctx *Context) error) {
	r.appRoute(http.MethodPatch, patten, handler)
}

// OPTION 注册 option 请求
func (r *RouterGroup) OPTION(patten string, handler func(ctx *Context) error) {
	r.appRoute("OPTION", patten, handler)
}

// DELETE 注册 delete 请求
func (r *RouterGroup) DELETE(patten string, handler func(ctx *Context) error) {
	r.appRoute("DELETE", patten, handler)
}

// ALL 兼容所有请求
func (r *RouterGroup) ALL(patten string, handler func(ctx *Context) error) {
	r.appRoute("ALL", patten, handler)
}

// addRoute 添加路由信息
func (r *RouterGroup) appRoute(method string, patten string, h HandlerFunc) {
	patten = strings.TrimRight(r.prefix+patten, "/")
	// Validate patten
	if patten == "" {
		patten = "/"
	}
	if patten[0] != '/' {
		patten = "/" + patten
	}
	internal.ConsoleRouter(method, patten)

	pnames := []string{} // Param names
	ppath := patten      // Pristine path
	for i, l := 0, len(patten); i < l; i++ {
		if patten[i] == ':' {
			j := i + 1

			r.insert(method, patten[:i], nil, skind, "", nil, nil)
			for ; i < l && patten[i] != '/'; i++ {
			}

			pnames = append(pnames, patten[j:i])

			patten = patten[:j] + patten[i:]
			i, l = j, len(patten)

			if i == l {
				r.insert(method, patten[:i], h, pkind, ppath, pnames, r.middlewares)
				return
			}
			r.insert(method, patten[:i], nil, pkind, "", nil, nil)

		} else if patten[i] == '*' {
			r.insert(method, patten[:i], nil, skind, "", nil, nil)
			pnames = append(pnames, "*")
			r.insert(method, patten[:i+1], h, akind, ppath, pnames, r.middlewares)

			return
		}
	}
	r.insert(method, patten, h, skind, ppath, pnames, r.middlewares)
}

func (r *RouterGroup) appendMiddleware(ppath string, m []MiddleWare) {
	if ppath == "" || len(m) == 0 {
		return
	}

	routers.lock.Lock()
	defer routers.lock.Unlock()
	routers.middlewareMap[ppath] = m
}

func (r *RouterGroup) insert(method, patten string, h HandlerFunc, t kind, ppath string, pnames []string, m []MiddleWare) {
	//save middleware
	r.appendMiddleware(ppath, m)

	// Adjust max param
	l := len(pnames)
	if *routers.maxParam < l {
		*routers.maxParam = l
	}

	cn := routers.tree // Current node as root
	if cn == nil {
		panic("orange: invalid method")
	}
	search := patten

	for {
		sl := len(search)
		pl := len(cn.prefix)
		l := 0

		// LCP
		max := pl
		if sl < max {
			max = sl
		}
		for ; l < max && search[l] == cn.prefix[l]; l++ {
		}
		if l == 0 {
			// At root node
			cn.label = search[0]
			cn.prefix = search

			if h != nil {
				cn.kind = t
				cn.addHandlerAndMiddleware(method, h)
				cn.ppath = ppath
				cn.pnames = pnames
			}
		} else if l < pl {
			// Split node
			n := newNode(cn.kind, cn.prefix[l:], cn, cn.children, cn.methodHandler, cn.ppath, cn.pnames)
			// Reset parent node
			cn.kind = skind
			cn.label = cn.prefix[0]
			cn.prefix = cn.prefix[:l]
			cn.children = nil
			cn.methodHandler = new(methodHandler)
			cn.ppath = ""
			cn.pnames = nil

			cn.addChild(n)

			if l == sl {
				// At parent node
				cn.kind = t
				cn.addHandlerAndMiddleware(method, h)
				cn.ppath = ppath
				cn.pnames = pnames
			} else {
				// Create child node
				n = newNode(t, search[l:], cn, nil, new(methodHandler), ppath, pnames)
				n.addHandlerAndMiddleware(method, h)
				cn.addChild(n)
			}
		} else if l < sl {
			search = search[l:]
			c := cn.findChildWithLabel(search[0])
			if c != nil {
				cn = c
				continue
			}

			// Create child node
			n := newNode(t, search, cn, nil, new(methodHandler), ppath, pnames)
			n.addHandlerAndMiddleware(method, h)
			cn.addChild(n)
		} else {
			// Node already exists
			if h != nil {
				cn.addHandlerAndMiddleware(method, h)
				cn.ppath = ppath
				if len(cn.pnames) == 0 {
					cn.pnames = pnames
				}
			}
		}
		return
	}
}

func (n *node) findChildWithLabel(l byte) *node {
	for _, c := range n.children {
		if c.label == l {
			return c
		}
	}
	return nil
}

func (n *node) addChild(c *node) {
	n.children = append(n.children, c)
}

func (n *node) addHandlerAndMiddleware(method string, h HandlerFunc) {
	switch method {
	case http.MethodConnect:
		n.methodHandler.connect = h
	case http.MethodDelete:
		n.methodHandler.delete = h
	case http.MethodGet:
		n.methodHandler.get = h
	case http.MethodHead:
		n.methodHandler.head = h
	case http.MethodOptions:
		n.methodHandler.options = h
	case http.MethodPatch:
		n.methodHandler.patch = h
	case http.MethodPost:
		n.methodHandler.post = h
	case http.MethodPut:
		n.methodHandler.put = h
	case http.MethodTrace:
		n.methodHandler.trace = h
	case "ALL":
		n.methodHandler.any = h
	}
}

func newNode(t kind, pre string, p *node, c children, mh *methodHandler, ppath string, pnames []string) *node {
	return &node{
		kind:          t,
		label:         pre[0],
		prefix:        pre,
		parent:        p,
		children:      c,
		ppath:         ppath,
		pnames:        pnames,
		methodHandler: mh,
	}
}

func getPath(r *http.Request) string {
	path := r.URL.RawPath
	if path == "" {
		path = r.URL.Path
	}
	return path
}
